import asyncio
import logging
from pathlib import Path

import sentry_sdk
from aiogram import Bot, Dispatcher
from aiogram.fsm.storage.memory import MemoryStorage, SimpleEventIsolation
from aiogram.fsm.storage.redis import DefaultKeyBuilder, RedisStorage
from aiogram_dialog import setup_dialogs
from arq import create_pool
from sentry_sdk.integrations.aiohttp import AioHttpIntegration

from src.config import load_config
from src.infrastructure.db.main import build_sa_engine, build_sa_session_factory
from src.infrastructure.logger import configure_logging
from src.infrastructure.payment_client import PayPalychClient
from src.presentation.tg_bot.dialogs import register_dialogs
from src.presentation.tg_bot.handlers import register_handlers
from src.presentation.tg_bot.middlewares.locale import LocaleMiddleware
from src.presentation.tg_bot.middlewares.uow import UowMiddleware
from src.presentation.tg_bot.services.locale import Localizator, configure_localizator
from src.presentation.tg_bot.services.set_commands import set_commands
from src.presentation.tg_bot.services.tg_helpers import broadcast

BOTFOLDER = Path(__file__).parent

logger = logging.getLogger(__name__)


def setup_middlewares(
    dp: Dispatcher,
    session_factory,
    localizator: Localizator,
):
    dp.message.outer_middleware(UowMiddleware(session_factory))
    dp.callback_query.outer_middleware(UowMiddleware(session_factory))
    dp.update.outer_middleware(UowMiddleware(session_factory))
    dp.message.outer_middleware.register(LocaleMiddleware(localizator))
    dp.callback_query.outer_middleware.register(LocaleMiddleware(localizator))


async def main():
    config = load_config()
    configure_logging()

    logger.error("Starting bot")
    if config.tg_bot.use_redis:
        storage = RedisStorage.from_url(
            url=config.redis.url,
            key_builder=DefaultKeyBuilder(with_destiny=True),
        )
    else:
        storage = MemoryStorage()

    bot = Bot(token=config.tg_bot.token, parse_mode="HTML")
    dp = Dispatcher(storage=storage, events_isolation=SimpleEventIsolation())
    engine = build_sa_engine(config.db)
    session_factory = build_sa_session_factory(engine)
    localizator = configure_localizator(BOTFOLDER / Path("locales/"))
    payment_client = PayPalychClient(api_token=config.api.token, shop_id=config.api.shop_id)
    redis_pool = await create_pool(config.redis.pool_settings)

    setup_middlewares(dp, session_factory, localizator)

    sentry_sdk.init(
        dsn="https://1bb4c736bdf557106ee2398731aaf952@o4505568395788288.ingest.sentry.io/4505704773320704",
        integrations=[
            AioHttpIntegration(),
        ],
        # Set traces_sample_rate to 1.0 to capture 100%
        # of transactions for performance monitoring.
        # We recommend adjusting this value in production,
        traces_sample_rate=1.0,
    )

    register_handlers(dp, config)
    setup_dialogs(dp)
    register_dialogs(dp)

    await set_commands(bot)
    await broadcast(bot, config.tg_bot.admin_ids, "Бот запущен!")
    try:
        await dp.start_polling(bot, config=config, payment_client=payment_client, arqredis=redis_pool)
    finally:
        await bot.session.close()


def cli():
    """Wrapper for command line, app's entry point"""
    try:
        asyncio.run(main())
    except (KeyboardInterrupt, SystemExit):
        logger.error("Bot stopped!")


if __name__ == "__main__":
    cli()
